﻿using System;
using System.Collections.Generic;

namespace Normalify
{
	public class BusinessRuleCollection
	{
		private readonly IDictionary<MultiKey<object>, Statistics> _rules;

		public IDictionary<MultiKey<object>, Statistics> Rules
		{
			get { return _rules; }
		}

		internal BusinessRuleCollection()
		{
			_rules = new Dictionary<MultiKey<object>, Statistics>();
		}

		internal Statistics Update(IList<Kvp<object>> vals, object factValue)
		{
			var key = new MultiKey<object>(vals);
			if (!_rules.ContainsKey(key))
			{
				_rules.Add(key, new Statistics());
			}
			var stat = _rules[key];
			return stat == null ? stat : UpdateStat(stat, Convert.ToDouble(factValue));
		}

		private Statistics UpdateStat(Statistics stat, double value)
		{
			if (Math.Abs(value - default(double)) < 0.00000000001)
			{
				stat.Zeros++;
				return stat;
			}
			stat.Min = Math.Min(stat.Min, value);
			stat.Max = Math.Max(stat.Max, value);
			stat.Sum += value;
			stat.Count++;

			//M2-4 are calculated to enable calculation of skewness and kurtosis
			//Refer to :http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Higher-order_statistics
			//for more information.
			var delta = value - stat.Mean;
			var deltaN = delta/stat.Count;
			var deltaN2 = deltaN*deltaN;
			var term1 = delta*deltaN*(stat.Count - 1);
			stat.Mean += deltaN;
			stat.M4 += term1*deltaN2*(stat.Count*stat.Count - 3*stat.Count + 3) + 6*
			                                                                      deltaN2*stat.M2 - 4*deltaN*stat.M3;
			stat.M3 += term1*deltaN*(stat.Count - 2) - 3*deltaN*stat.M2;
			stat.M2 += term1;
			return stat;
		}
	}
}